Note: This tutorial assumes you are comfortable with using roscpp, and have gone through the ROS/Tutorials/CreatingPackage, ROS/Tutorials/WritingPublisherSubscriber(c++) and pluginlib/Tutorials/Writing and Using a Simple Plugin. |
Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags. |
Writing a C++ Plugin
Description: Shows how to write a plugin for rqt in C++.Keywords: rqt tutorial
Tutorial Level: INTERMEDIATE
Next Tutorial: rqt/Tutorials/Using .ui file in rqt plugin rqt/Tutorials/Create rqt plugin using an existing Qt based tool
Contents
Intro
This tutorial will show you how to write a C++ plugin to integrate a custom user interface into rqt. You're expected to come from Create your new rqt plugin tutorial. Complete example of all files in a package is available on github. If you are using QtCreator you can generate the skeleton code (described below) for the plugin with this rqt plugin wizard.
Particularly in CMakeLists.txt you'll see C++ specifics. Refer to existing plugins that are written in C++ rqt_image_view, rqt_rviz to get a better idea.
Create plugin description file
Basically the same step described in previous tutorial. Some unique cases:
Replace rqt_gui_py with rqt_gui_cpp
C++ binding specific note for xml attribute:
/library/class@name
The name of the plugin that is exported via the PLUGINLIB_DECLARE_CLASS macro.
The code
Create the src/my_namespace/my_plugin.h file and paste the following inside it:
1 #ifndef my_namespace__my_plugin_H
2 #define my_namespace__my_plugin_H
3
4 #include <rqt_gui_cpp/plugin.h>
5 #include <my_namespace/ui_my_plugin.h>
6 #include <QWidget>
7
8 namespace my_namespace {
9
10 class MyPlugin
11 : public rqt_gui_cpp::Plugin
12 {
13 Q_OBJECT
14 public:
15 MyPlugin();
16 virtual void initPlugin(qt_gui_cpp::PluginContext& context);
17 virtual void shutdownPlugin();
18 virtual void saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const;
19 virtual void restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings);
20
21 // Comment in to signal that the plugin has a way to configure it
22 //bool hasConfiguration() const;
23 //void triggerConfiguration();
24 private:
25 Ui::MyPluginWidget ui_;
26 QWidget* widget_;
27 };
28 } // namespace
29 #endif // my_namespace__my_plugin_H
30
Create the src/my_namespace/my_plugin.cpp file and paste the following inside it:
1 #include "my_plugin.h"
2 #include <pluginlib/class_list_macros.h>
3 #include <QStringList>
4
5 namespace my_namespace {
6
7 MyPlugin::MyPlugin()
8 : rqt_gui_cpp::Plugin()
9 , widget_(0)
10 {
11 // Constructor is called first before initPlugin function, needless to say.
12
13 // give QObjects reasonable names
14 setObjectName("MyPlugin");
15 }
16
17 void MyPlugin::initPlugin(qt_gui_cpp::PluginContext& context)
18 {
19 // access standalone command line arguments
20 QStringList argv = context.argv();
21 // create QWidget
22 widget_ = new QWidget();
23 // extend the widget with all attributes and children from UI file
24 ui_.setupUi(widget_);
25 // add widget to the user interface
26 context.addWidget(widget_);
27 }
28
29 void MyPlugin::shutdownPlugin()
30 {
31 // TODO unregister all publishers here
32 }
33
34 void MyPlugin::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const
35 {
36 // TODO save intrinsic configuration, usually using:
37 // instance_settings.setValue(k, v)
38 }
39
40 void MyPlugin::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings)
41 {
42 // TODO restore intrinsic configuration, usually using:
43 // v = instance_settings.value(k)
44 }
45
46 /*bool hasConfiguration() const
47 {
48 return true;
49 }
50
51 void triggerConfiguration()
52 {
53 // Usually used to open a dialog to offer the user a set of configuration
54 }*/
55
56 } // namespace
57 PLUGINLIB_DECLARE_CLASS(my_namespace, MyPlugin, my_namespace::MyPlugin, rqt_gui_cpp::Plugin)
This simple plugin only adds a single widget using addWidget. But a plugin can contribute multiple widgets by calling that method for each widget. The method can be called at any time to dynamically contribute more widgets. Internally each widget is embedded into a new QDockWidget which itself is added to the QMainWindow.
Using a UI file
The example uses an Qt Designer UI file to describe the widget tree. Instead of hand-coding the widget tree the Qt meta object compiler generates a header file at compile time which includes all the widgets based on the description from the file. For more detail, see rqt/Tutorials/Using .ui file in rqt plugin
Special note for using roscpp with rqt
The plugin should not call init_node as this is performed by rqt_gui_cpp. The plugin can use any roscpp-specific functionality (like Publishers, Subscribers, Parameters). Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method.
A C++ plugin inherits from Nodelet and therefore gains the same advantages (i.e. enables exchanging boost>::shared_pointer).
Due to restrictions in Qt, you cannot manipulate Qt widgets directly within ROS callbacks, because they are running in a different thread. In the ROS callback you can:
- emit a Qt signal (which will bridge across the threads) and manipulate the widgets in the receiving slot
OR
only operate on non-widget entities like QAbstractItemModels